home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / battleship / bs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  24.6 KB  |  1,126 lines

  1. /* 
  2.  * bs.c - original author: Bruce Holloway
  3.  *        salvo option by: Chuck A DeGaul
  4.  * with improved user interface, autoconfiguration and code cleanup
  5.  *        by Eric S. Raymond
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include <curses.h>
  10. #include <signal.h>
  11. #include <ctype.h>
  12. #include <assert.h>
  13.  
  14. #ifndef A_UNDERLINE    /* BSD curses */
  15. #define    beep()    write(1,"\007",1);
  16. #define    saveterm savetty
  17. #define    resetterm resetty
  18. #ifndef cbreak        /* Sun curses.h has cbreak() */
  19. #define    cbreak    crmode
  20. #define    nocbreak nocrmode
  21. #endif
  22. #define strchr    index
  23. #define chtype char
  24. #define ungetch ungetc
  25. #else
  26. #define srand(n)    srand48(n)
  27. #define rand()        lrand48()
  28. extern long lrand48();
  29. extern void srand48();
  30. #define bzero(s, n)    (void)memset((char *)(s), '\0', n)
  31. extern char *memset();
  32. #endif
  33.  
  34. extern unsigned sleep();
  35. extern char *strchr(), *strcpy();
  36. extern long time();
  37. extern void exit();
  38.  
  39. /*
  40.  * Constants for tuning the random-fire algorithm. It prefers moves that
  41.  * diagonal-stripe the board with a stripe separation of srchstep. If
  42.  * no such preferred moves are found, srchstep is decremented.
  43.  */
  44. #define BEGINSTEP    3    /* initial value of srchstep */
  45.  
  46. /* miscellaneous constants */
  47. #define SHIPTYPES    5
  48. #define    OTHER        (1-turn)
  49. #define PLAYER        0
  50. #define COMPUTER    1
  51. #define FF        '\014'    /* used as redraw command */
  52. #define MARK_HIT    'H'
  53. #define MARK_MISS    'o'
  54.  
  55. /* coordinate handling */
  56. #define BWIDTH        10
  57. #define BDEPTH        10
  58.  
  59. /* display symbols */
  60. #define SHOWHIT        '*'
  61. #define SHOWSPLASH    ' '
  62. #define IS_SHIP(c)    isupper(c)
  63.  
  64. /* how to position us on player board */
  65. #define PYBASE    3
  66. #define PXBASE    3
  67. #define PY(y)    (PYBASE + (y))
  68. #define PX(x)    (PXBASE + (x)*3)
  69. #define pgoto(y, x)    (void)move(PY(y), PX(x))
  70.  
  71. /* how to position us on cpu board */
  72. #define CYBASE    3
  73. #define CXBASE    48
  74. #define CY(y)    (CYBASE + (y))
  75. #define CX(x)    (CXBASE + (x)*3)
  76. #define cgoto(y, x)    (void)move(CY(y), CX(x))
  77.  
  78. #define ONBOARD(x, y)    (x >= 0 && x < BWIDTH && y >= 0 && y < BDEPTH)
  79.  
  80. /* other board locations */
  81. #define COLWIDTH    80
  82. #define PROMPTLINE    21            /* prompt line */
  83. #define SYBASE        CYBASE + BDEPTH + 3    /* move key diagram */
  84. #define SXBASE        63
  85. #define MYBASE        SYBASE - 1        /* diagram caption */
  86. #define MXBASE        64
  87. #define HYBASE        SYBASE - 1        /* help area */
  88. #define HXBASE        0
  89.  
  90. /* this will need to be changed if BWIDTH changes */
  91. static char numbers[] = "   0  1  2  3  4  5  6  7  8  9";
  92.  
  93. static char carrier[] = "Aircraft Carrier";
  94. static char battle[] = "Battleship";
  95. static char sub[] = "Submarine";
  96. static char destroy[] = "Destroyer";
  97. static char ptboat[] = "PT Boat";
  98.  
  99. static char name[40];
  100. static char dftname[] = "Stranger";
  101.  
  102. /* direction constants */
  103. #define E    0
  104. #define SE    1
  105. #define S    2
  106. #define SW    3
  107. #define W    4
  108. #define NW    5
  109. #define N    6
  110. #define NE    7
  111. static int xincr[8] = {1,  1,  0, -1, -1, -1,  0,  1};
  112. static int yincr[8] = {0,  1,  1,  1,  0, -1, -1, -1};
  113.  
  114. /* current ship position and direction */
  115. static int curx = (BWIDTH / 2);
  116. static int cury = (BDEPTH / 2);
  117.  
  118. typedef struct
  119. {
  120.     char *name;        /* name of the ship type */
  121.     unsigned hits;    /* how many times has this ship been hit? */
  122.     char symbol;    /* symbol for game purposes */
  123.     char length;    /* length of ship */
  124.     char x, y;        /* coordinates of ship start point */
  125.     char dir;        /* direction of `bow' */
  126.     bool placed;    /* has it been placed on the board? */
  127. }
  128. ship_t;
  129.  
  130. ship_t plyship[SHIPTYPES] =
  131. {
  132.     { carrier,    0, 'A', 5},
  133.     { battle,    0, 'B', 4},
  134.     { destroy,    0, 'D', 3},
  135.     { sub,    0, 'S', 3},
  136.     { ptboat,    0, 'P', 2},
  137. };
  138.  
  139. ship_t cpuship[SHIPTYPES] =
  140. {
  141.     { carrier,    0, 'A', 5},
  142.     { battle,    0, 'B', 4},
  143.     { destroy,    0, 'D', 3},
  144.     { sub,    0, 'S', 3},
  145.     { ptboat,    0, 'P', 2},
  146. };
  147.  
  148. /* "Hits" board, and main board. */
  149. static char hits[2][BWIDTH][BDEPTH], board[2][BWIDTH][BDEPTH];
  150.  
  151. static int turn;            /* 0=player, 1=computer */
  152. static int plywon=0, cpuwon=0;        /* How many games has each won? */
  153.  
  154. static int salvo, blitz, closepack;
  155.  
  156. #define    PR    (void)addstr
  157.  
  158. static void uninitgame()
  159. /* end the game, either normally or due to signal */
  160. {
  161.     clear();
  162.     (void)refresh();
  163.     (void)resetterm();
  164.     (void)echo();
  165.     (void)endwin();
  166.     exit(0);
  167. }
  168.  
  169. static void announceopts()
  170. /* announce which game options are enabled */
  171. {
  172.     if (salvo || blitz || closepack)
  173.     {
  174.     (void) printw("Playing optional game (");
  175.     if (salvo)
  176.         (void) printw("salvo, ");
  177.     else
  178.         (void) printw("nosalvo, ");
  179.     if (blitz)
  180.         (void) printw("blitz ");
  181.     else
  182.         (void) printw("noblitz, ");
  183.     if (closepack)
  184.         (void) printw("closepack)");
  185.     else
  186.         (void) printw("noclosepack)");
  187.     }
  188.     else
  189.     (void) printw(
  190.     "Playing standard game (noblitz, nosalvo, noclosepack)");
  191. }
  192.  
  193. static void intro()
  194. {
  195.     extern char *getlogin();
  196.     char *tmpname;
  197.  
  198.     srand(time(0L)+getpid());    /* Kick the random number generator */
  199.  
  200.     (void) signal(SIGINT,uninitgame);
  201.     (void) signal(SIGINT,uninitgame);
  202.     (void) signal(SIGIOT,uninitgame);        /* for assert(3) */
  203.     if(signal(SIGQUIT,SIG_IGN) != SIG_IGN)
  204.     (void)signal(SIGQUIT,uninitgame);
  205.  
  206.     if(tmpname = getlogin())
  207.     (void)strcpy(name,tmpname);
  208.     else
  209.     (void)strcpy(name,dftname);
  210.     name[0] = toupper(name[0]);
  211.  
  212.     (void)initscr();
  213.     (void)saveterm();
  214.     (void)nonl();
  215.     (void)cbreak();
  216.     (void)noecho();
  217.  
  218. #ifdef PENGUIN
  219.     (void)clear();
  220.     (void)mvaddstr(4,29,"Welcome to Battleship!");
  221.     (void)move(8,0);
  222.     PR("                                                  \\\n");
  223.     PR("                           \\                     \\ \\\n");
  224.     PR("                          \\ \\                   \\ \\ \\_____________\n");
  225.     PR("                         \\ \\ \\_____________      \\ \\/            |\n");
  226.     PR("                          \\ \\/             \\      \\/             |\n");
  227.     PR("                           \\/               \\_____/              |__\n");
  228.     PR("           ________________/                                       |\n");
  229.     PR("           \\  S.S. Penguin                                         |\n");
  230.     PR("            \\                                                     /\n");
  231.     PR("             \\___________________________________________________/\n");
  232.  
  233.     (void) mvaddstr(22,27,"Hit any key to continue..."); (void)refresh();
  234.     (void) getch();
  235. #endif /* PENGUIN */
  236.  
  237.     (void) clear();
  238.     (void) mvaddstr(0,35,"BATTLESHIPS");
  239.     (void) move(PROMPTLINE + 2, 0);
  240.     announceopts();
  241.  
  242.     (void) mvaddstr(MYBASE,   MXBASE, "Aiming keys:");
  243.     (void) mvaddstr(SYBASE,   SXBASE, "y k u    7 8 9");
  244.     (void) mvaddstr(SYBASE+1, SXBASE, " \\|/      \\|/ ");
  245.     (void) mvaddstr(SYBASE+2, SXBASE, "h-+-l    4-+-6");
  246.     (void) mvaddstr(SYBASE+3, SXBASE, " /|\\      /|\\ ");
  247.     (void) mvaddstr(SYBASE+4, SXBASE, "b j n    1 2 3");
  248. }            
  249.  
  250. /* VARARGS1 */
  251. static void prompt(n, f, s)
  252. /* print a message at the prompt line */
  253. int n;
  254. char *f, *s;
  255. {
  256.     (void) move(PROMPTLINE + n, 0);
  257.     (void) clrtoeol();
  258.     (void) printw(f, s);
  259.     (void) refresh();
  260. }
  261.  
  262. static void error(s)
  263. char *s;
  264. {
  265.     (void) move(PROMPTLINE + 2, 0);
  266.     (void) clrtoeol();
  267.     if (s)
  268.     {
  269.     (void) addstr(s);
  270.     (void) beep();
  271.     }
  272. }
  273.  
  274. static void placeship(b, ss, vis)
  275. int b;
  276. ship_t *ss;
  277. int vis;
  278. {
  279.     int l;
  280.  
  281.     for(l = 0; l < ss->length; ++l)
  282.     {
  283.     int newx = ss->x + l * xincr[ss->dir];
  284.     int newy = ss->y + l * yincr[ss->dir];
  285.  
  286.     board[b][newx][newy] = ss->symbol;
  287.     if (vis)
  288.     {
  289.         pgoto(newy, newx);
  290.         (void) addch((chtype)ss->symbol);
  291.     }
  292.     }
  293.     ss->hits = 0;
  294. }
  295.  
  296. static void randomplace(b, ss)
  297. /* generate a valid random ship placement into px,py */
  298. int b;
  299. ship_t *ss;
  300. {
  301.     register int bwidth = BWIDTH - ss->length;
  302.     register int bdepth = BDEPTH - ss->length;
  303.  
  304.     do {
  305.     ss->y = rnd(bdepth);
  306.     ss->x = rnd(bwidth);
  307.     ss->dir = rnd(2) ? E : S;
  308.     } while
  309.     (!checkplace(b, ss, FALSE));
  310. }
  311.  
  312. static void initgame()
  313. {
  314.     int i, unplaced;
  315.     ship_t *ss;
  316.  
  317.     bzero(board, sizeof(char) * BWIDTH * BDEPTH * 2);
  318.     bzero(hits, sizeof(char) * BWIDTH * BDEPTH * 2);
  319.     for (i = 0; i < SHIPTYPES; i++)
  320.     {
  321.     ss = cpuship + i;
  322.     ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0;
  323.     ss = plyship + i;
  324.     ss->x = ss->y = ss->dir = ss->hits = ss->placed = 0;
  325.     }
  326.  
  327.     /* draw empty boards */
  328.     (void) mvaddstr(PYBASE - 2, PXBASE + 5, "Main Board");
  329.     (void) mvaddstr(PYBASE - 1, PXBASE - 3,numbers);
  330.     for(i=0; i < BDEPTH; ++i)
  331.     (void) mvprintw(PYBASE + i, PXBASE - 3,
  332.          "%c  .  .  .  .  .  .  .  .  .  .  %c",i+'A',i+'A');
  333.     (void) mvaddstr(PYBASE + BDEPTH, PXBASE - 3,numbers);
  334.     (void) mvaddstr(CYBASE - 2, CXBASE + 7,"Hit/Miss Board");
  335.     (void) mvaddstr(CYBASE - 1, CXBASE - 3, numbers);
  336.     for(i=0; i < BDEPTH; ++i)
  337.     (void) mvprintw(CYBASE + i, CXBASE - 3,
  338.          "%c  .  .  .  .  .  .  .  .  .  .  %c",i+'A',i+'A');
  339.     (void) mvaddstr(CYBASE + BDEPTH,CXBASE - 3,numbers);
  340.  
  341.     (void) mvprintw(HYBASE,  HXBASE,
  342.             "To position your ships: move the cursor to a spot, then");
  343.     (void) mvprintw(HYBASE+1,HXBASE,
  344.             "type the first letter of a ship type to select it, then");
  345.     (void) mvprintw(HYBASE+2,HXBASE,
  346.             "type a direction ([hjkl] or [4862]), indicating how the");
  347.     (void) mvprintw(HYBASE+3,HXBASE,
  348.             "ship should be pointed. You may also type a ship letter");
  349.     (void) mvprintw(HYBASE+4,HXBASE,
  350.             "followed by `r' to position it randomly, or type `R' to");
  351.     (void) mvprintw(HYBASE+5,HXBASE,
  352.             "place all remaining ships randomly.");
  353.  
  354.     /* have the computer place ships */
  355.     for(ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
  356.     {
  357.     randomplace(COMPUTER, ss);
  358.     placeship(COMPUTER, ss, FALSE);
  359.     }
  360.  
  361.     ss = (ship_t *)NULL;
  362.     do {
  363.     extern char *strchr();
  364.     char c, docked[SHIPTYPES + 2], *cp = docked;
  365.  
  366.     /* figure which ships still wait to be placed */
  367.     *cp++ = 'R';
  368.     for (i = 0; i < SHIPTYPES; i++)
  369.         if (!plyship[i].placed)
  370.         *cp++ = plyship[i].symbol;
  371.     *cp = '\0';
  372.  
  373.     /* get a command letter */
  374.     prompt(1, "Type one of [%s] to pick a ship.", docked+1);
  375.     do {
  376.         c = getcoord(PLAYER);
  377.     } while
  378.         (!strchr(docked, c));
  379.  
  380.     if (c == 'R')
  381.         (void) ungetch('R');
  382.     else
  383.     {
  384.         /* map that into the corresponding symbol */
  385.         for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
  386.         if (ss->symbol == c)
  387.             break;
  388.  
  389.         prompt(1, "Type one of [hjklrR] to place your %s.", ss->name);
  390.         pgoto(cury, curx);
  391.     }
  392.  
  393.     do {
  394.         c = getch();
  395.     } while
  396.         (!strchr("hjklrR", c) || c == FF);
  397.  
  398.     if (c == FF)
  399.     {
  400.         (void)clearok(stdscr, TRUE);
  401.         (void)refresh();
  402.     }
  403.     else if (c == 'r')
  404.     {
  405.         prompt(1, "Random-placing your %s", ss->name);
  406.         randomplace(PLAYER, ss);
  407.         placeship(PLAYER, ss, TRUE);
  408.         error((char *)NULL);
  409.         ss->placed = TRUE;
  410.     }        
  411.     else if (c == 'R')
  412.     {
  413.         prompt(1, "Placing the rest of your fleet at random...");
  414.         for (ss = plyship; ss < plyship + SHIPTYPES; ss++)
  415.         if (!ss->placed)
  416.         {
  417.             randomplace(PLAYER, ss);
  418.             placeship(PLAYER, ss, TRUE);
  419.             ss->placed = TRUE;
  420.         }
  421.         error((char *)NULL);
  422.     }        
  423.     else if (strchr("hjkl8462", c))
  424.     {
  425.         ss->x = curx;
  426.         ss->y = cury;
  427.  
  428.         switch(c)
  429.         {
  430.         case 'k': case '8': ss->dir = N; break;
  431.         case 'j': case '2': ss->dir = S; break;
  432.         case 'h': case '4': ss->dir = W; break;
  433.         case 'l': case '6': ss->dir = E; break;
  434.         }        
  435.  
  436.         if (checkplace(PLAYER, ss, TRUE))
  437.         {
  438.         placeship(PLAYER, ss, TRUE);
  439.         error((char *)NULL);
  440.         ss->placed = TRUE;
  441.         }
  442.     }
  443.  
  444.     for (unplaced = i = 0; i < SHIPTYPES; i++)
  445.         unplaced += !plyship[i].placed;
  446.     } while
  447.     (unplaced);
  448.  
  449.     turn = rnd(2);
  450.  
  451.     (void) mvprintw(HYBASE,  HXBASE,
  452.             "To fire, move the cursor to your chosen aiming point   ");
  453.     (void) mvprintw(HYBASE+1,  HXBASE,
  454.             "and strike any key other than a motion key.            ");
  455.     (void) mvprintw(HYBASE+2,  HXBASE,
  456.             "                                                       ");
  457.     (void) mvprintw(HYBASE+3,  HXBASE,
  458.             "                                                       ");
  459.     (void) mvprintw(HYBASE+4,  HXBASE,
  460.             "                                                       ");
  461.     (void) mvprintw(HYBASE+5,  HXBASE,
  462.             "                                                       ");
  463.  
  464.     (void) prompt(0, "Press any key to start...");
  465.     (void) getch();
  466. }
  467.  
  468. static int rnd(n)
  469. int n;
  470. {
  471.     return(((rand() & 0x7FFF) % n));
  472. }
  473.  
  474. static int getcoord(atcpu)
  475. int atcpu;
  476. {
  477.     int ny, nx, c;
  478.  
  479.     if (atcpu)
  480.     cgoto(cury,curx);
  481.     else
  482.     pgoto(cury, curx);
  483.     (void)refresh();
  484.     for (;;)
  485.     {
  486.     if (atcpu)
  487.     {
  488.         (void) mvprintw(CYBASE + BDEPTH+1, CXBASE+11, "(%d, %c)", curx, 'A'+cury);
  489.         cgoto(cury, curx);
  490.     }
  491.     else
  492.     {
  493.         (void) mvprintw(PYBASE + BDEPTH+1, PXBASE+11, "(%d, %c)", curx, 'A'+cury);
  494.         pgoto(cury, curx);
  495.     }
  496.     (void)refresh();
  497.  
  498.     switch(c = getch())
  499.     {
  500.     case 'k': case '8': ny = cury+BDEPTH-1; nx = curx;   break;
  501.     case 'j': case '2': ny = cury+1;        nx = curx;   break;
  502.     case 'h': case '4': ny = cury;          nx = curx+BWIDTH-1; break;
  503.     case 'l': case '6': ny = cury;          nx = curx+1; break;
  504.     case 'y': case '7': ny = cury+BDEPTH-1; nx = curx+BWIDTH-1; break;
  505.     case 'b': case '1': ny = cury+1;        nx = curx+BWIDTH-1; break;
  506.     case 'u': case '9': ny = cury+BDEPTH-1; nx = curx+1; break;
  507.     case 'n': case '3': ny = cury+1;        nx = curx+1; break;
  508.     case '\014': (void)clearok(stdscr, TRUE); (void)refresh(); break;
  509.     default:
  510.         if (atcpu)
  511.         (void) mvaddstr(CYBASE + BDEPTH + 1, CXBASE + 11, "      ");
  512.         else
  513.         (void) mvaddstr(PYBASE + BDEPTH + 1, PXBASE + 11, "      ");
  514.         return(c);
  515.     }
  516.  
  517.     curx = nx % BWIDTH;
  518.     cury = ny % BDEPTH;
  519.     }
  520. }
  521.  
  522. static int collidecheck(b, y, x)
  523. /* is this location on the selected zboard adjacent to a ship? */
  524. int b;
  525. int y, x;
  526. {
  527.     int    collide;
  528.  
  529.     /* anything on the square */
  530.     if (collide = IS_SHIP(board[b][x][y]))
  531.     return(collide);
  532.  
  533.     /* anything on the neighbors */
  534.     if (!closepack)
  535.     {
  536.     int i;
  537.  
  538.     for (i = 0; i < 8; i++)
  539.     {
  540.         int xend, yend;
  541.  
  542.         yend = y + yincr[i];
  543.         xend = x + xincr[i];
  544.         if (ONBOARD(xend, yend))
  545.         collide += IS_SHIP(board[b][xend][yend]);
  546.     }
  547.     }
  548.     return(collide);
  549. }
  550.  
  551. static int checkplace(b, ss, vis)
  552. int b;
  553. ship_t *ss;
  554. int vis;
  555. {
  556.     int l, xend, yend;
  557.  
  558.     /* first, check for board edges */
  559.     xend = ss->x + ss->length * xincr[ss->dir];
  560.     yend = ss->y + ss->length * yincr[ss->dir];
  561.     if (!ONBOARD(xend, yend))
  562.     {
  563.     if(vis)
  564.         switch(rnd(3))
  565.         {
  566.         case 0:
  567.         error("Ship is hanging from the edge of the world");
  568.         break;
  569.         case 1:
  570.         error("Try fitting it on the board");
  571.         break;
  572.         case 2:
  573.         error("Figure I won't find it if you put it there?");
  574.         break;
  575.         }
  576.     return(0);
  577.     }
  578.  
  579.     for(l = 0; l < ss->length; ++l)
  580.     {
  581.     if(collidecheck(b, ss->y+l*yincr[ss->dir], ss->x+l*xincr[ss->dir]))
  582.     {
  583.         if (vis)
  584.         switch(rnd(3))
  585.         {
  586.             case 0:
  587.             error("There's already a ship there");
  588.             break;
  589.             case 1:
  590.             error("Collision alert! Aaaaaagh!");
  591.             break;
  592.             case 2:
  593.             error("Er, Admiral, what about the other ship?");
  594.             break;
  595.             }
  596.         return(0);
  597.         }
  598.     }
  599.     return(1);
  600. }
  601.  
  602. static int awinna()
  603. {
  604.     int i, j;
  605.     ship_t *ss;
  606.  
  607.     for(i=0; i<2; ++i)
  608.     {
  609.     ss = (i) ? cpuship : plyship;
  610.     for(j=0; j < SHIPTYPES; ++j, ++ss)
  611.         if(ss->length > ss->hits)
  612.         break;
  613.     if (j == SHIPTYPES)
  614.         return(OTHER);
  615.     }
  616.     return(-1);
  617. }
  618.  
  619. static ship_t *hitship(x, y)
  620. /* register a hit on the targeted ship */
  621. int x, y;
  622. {
  623.     ship_t *sb, *ss;
  624.     char sym;
  625.     int oldx, oldy;
  626.  
  627.     getyx(stdscr, oldy, oldx);
  628.     sb = (turn) ? plyship : cpuship;
  629.     if(!(sym = board[OTHER][x][y]))
  630.     return((ship_t *)NULL);
  631.     for(ss = sb; ss < sb + SHIPTYPES; ++ss)
  632.     if(ss->symbol == sym)
  633.     {
  634.         if (++ss->hits < ss->length)    /* still afloat? */
  635.         return((ship_t *)NULL);
  636.         else                /* sunk! */
  637.         {
  638.         int i, j;
  639.  
  640.         if (!closepack)
  641.             for (j = -1; j <= 1; j++)
  642.             {
  643.             int bx = ss->x + j * xincr[(ss->dir + 2) % 8];
  644.             int by = ss->y + j * yincr[(ss->dir + 2) % 8];
  645.  
  646.             for (i = -1; i <= ss->length; ++i)
  647.             {
  648.                 int x, y;
  649.                 
  650.                 x = bx + i * xincr[ss->dir];
  651.                 y = by + i * yincr[ss->dir];
  652.                 if (ONBOARD(x, y))
  653.                 {
  654.                 hits[turn][x][y] = MARK_MISS;
  655.                 if (turn % 2 == PLAYER)
  656.                 {
  657.                     cgoto(y, x);
  658.                     (void)addch(MARK_MISS);
  659.                 }
  660.                 }
  661.             }
  662.             }
  663.  
  664.         for (i = 0; i < ss->length; ++i)
  665.         {
  666.             int x = ss->x + i * xincr[ss->dir];
  667.             int y = ss->y + i * yincr[ss->dir];
  668.  
  669.             hits[turn][x][y] = ss->symbol;
  670.             if (turn % 2 == PLAYER)
  671.             {
  672.             cgoto(y, x);
  673.             (void) addch(ss->symbol);
  674.             }
  675.         }
  676.  
  677.         (void) move(oldy, oldx);
  678.         return(ss);
  679.         }
  680.     }
  681.     (void) move(oldy, oldx);
  682.     return((ship_t *)NULL);
  683. }
  684.  
  685. static int plyturn()
  686. {
  687.     ship_t *ss;
  688.     bool hit;
  689.     char *m;
  690.  
  691.     prompt(1, "Where do you want to shoot? ");
  692.     for (;;)
  693.     {
  694.     (void) getcoord(COMPUTER);
  695.     if (hits[PLAYER][curx][cury])
  696.     {
  697.         prompt(1, "You shelled this spot already! Try again.");
  698.         beep();
  699.     }
  700.     else
  701.         break;
  702.     }
  703.     hit = IS_SHIP(board[COMPUTER][curx][cury]);
  704.     hits[PLAYER][curx][cury] = hit ? MARK_HIT : MARK_MISS;
  705.     cgoto(cury, curx);
  706.     (void) addch((chtype)hits[PLAYER][curx][cury]);
  707.  
  708.     prompt(1, "You %s.", hit ? "scored a hit" : "missed");
  709.     if(hit && (ss = hitship(curx, cury)))
  710.     {
  711.     switch(rnd(5))
  712.     {
  713.     case 0:
  714.         m = " You sank my %s!";
  715.         break;
  716.     case 1:
  717.         m = " I have this sinking feeling about my %s....";
  718.         break;
  719.     case 2:
  720.         m = " My %s has gone to Davy Jones's locker!";
  721.         break;
  722.     case 3:
  723.         m = " Glub, glub -- my %s is headed for the bottom!";
  724.         break;
  725.     case 4:
  726.         m = " You'll pick up survivors from my my %s, I hope...!";
  727.         break;
  728.     }
  729.     (void)printw(m, ss->name);
  730.     (void)beep();
  731.     return(awinna() == -1);
  732.     }
  733.     return(hit);
  734. }
  735.  
  736. static int sgetc(s)
  737. char *s;
  738. {
  739.     char *s1;
  740.     int ch;
  741.  
  742.     (void)refresh();
  743.     for(;;)
  744.     {
  745.     ch = toupper(getch());
  746.     if (ch == 3)
  747.         uninitgame();
  748.     for (s1=s; *s1 && ch != *s1; ++s1)
  749.         continue;
  750.     if (*s1)
  751.     {
  752.         (void) addch((chtype)ch);
  753.         (void)refresh();
  754.         return(ch);
  755.         }
  756.     }
  757. }
  758.  
  759.  
  760. static void randomfire(px, py)
  761. /* random-fire routine -- implements simple diagonal-striping strategy */
  762. int    *px, *py;
  763. {
  764.     static int turncount = 0;
  765.     static int srchstep = BEGINSTEP;
  766.     static int huntoffs;        /* Offset on search strategy */
  767.     int ypossible[BWIDTH * BDEPTH], xpossible[BWIDTH * BDEPTH], nposs;
  768.     int ypreferred[BWIDTH * BDEPTH], xpreferred[BWIDTH * BDEPTH], npref;
  769.     int x, y, i;
  770.  
  771.     if (turncount++ == 0)
  772.     huntoffs = rnd(srchstep);
  773.  
  774.     /* first, list all possible moves */
  775.     nposs = npref = 0;
  776.     for (x = 0; x < BWIDTH; x++)
  777.     for (y = 0; y < BDEPTH; y++)
  778.         if (!hits[COMPUTER][x][y])
  779.         {
  780.         xpossible[nposs] = x;
  781.         ypossible[nposs] = y;
  782.         nposs++;
  783.         if (((x+huntoffs) % srchstep) != (y % srchstep))
  784.         {
  785.             xpreferred[npref] = x;
  786.             ypreferred[npref] = y;
  787.             npref++;
  788.         }
  789.         }
  790.  
  791.     if (npref)
  792.     {
  793.     i = rnd(npref);
  794.  
  795.     *px = xpreferred[i];
  796.     *py = ypreferred[i];
  797.     }
  798.     else if (nposs)
  799.     {
  800.     i = rnd(nposs);
  801.  
  802.     *px = xpossible[i];
  803.     *py = ypossible[i];
  804.  
  805.     if (srchstep > 1)
  806.         --srchstep;
  807.     }
  808.     else
  809.     {
  810.     error("No moves possible?? Help!");
  811.     exit(1);
  812.     /*NOTREACHED*/
  813.     }
  814. }
  815.  
  816. #define S_MISS    0
  817. #define S_HIT    1
  818. #define S_SUNK    -1
  819.  
  820. static bool cpufire(x, y)
  821. /* fire away at given location */
  822. int    x, y;
  823. {
  824.     bool hit, sunk;
  825.     ship_t *ss;
  826.  
  827.     hits[COMPUTER][x][y] = (hit = (board[PLAYER][x][y])) ? MARK_HIT : MARK_MISS;
  828.     (void) mvprintw(PROMPTLINE, 0,
  829.     "I shoot at %c%d. I %s!", y + 'A', x, hit ? "hit" : "miss");
  830.     if (sunk = (hit && (ss = hitship(x, y))))
  831.     (void) printw(" I've sunk your %s", ss->name);
  832.     (void)clrtoeol();
  833.  
  834.     pgoto(y, x);
  835.     (void)addch((chtype)(hit ? SHOWHIT : SHOWSPLASH));
  836.  
  837.     return(hit ? (sunk ? S_SUNK : S_HIT) : S_MISS);
  838. }
  839.  
  840. /*
  841.  * This code implements a fairly irregular FSM, so please forgive the rampant
  842.  * unstructuredness below. The five labels are states which need to be held
  843.  * between computer turns.
  844.  */
  845. static bool cputurn()
  846. {
  847. #define POSSIBLE(x, y)    (ONBOARD(x, y) && !hits[COMPUTER][x][y])
  848. #define RANDOM_FIRE    0
  849. #define RANDOM_HIT    1
  850. #define HUNT_DIRECT    2
  851. #define FIRST_PASS    3
  852. #define REVERSE_JUMP    4
  853. #define SECOND_PASS    5
  854.     static int next = RANDOM_FIRE;
  855.     static bool used[4];
  856.     static ship_t ts;
  857.     int navail, x, y, d, n, hit = S_MISS;
  858.  
  859.     switch(next)
  860.     {
  861.     case RANDOM_FIRE:    /* last shot was random and missed */
  862.     refire:
  863.     randomfire(&x, &y);
  864.     if (!(hit = cpufire(x, y)))
  865.         next = RANDOM_FIRE;
  866.     else
  867.     {
  868.         ts.x = x; ts.y = y;
  869.         ts.hits = 1;
  870.         next = (hit == S_SUNK) ? RANDOM_FIRE : RANDOM_HIT;
  871.     }
  872.     break;
  873.  
  874.     case RANDOM_HIT:    /* last shot was random and hit */
  875.     used[E/2] = used[S/2] = used[W/2] = used[N/2] = FALSE;
  876.     /* FALLTHROUGH */
  877.  
  878.     case HUNT_DIRECT:    /* last shot hit, we're looking for ship's long axis */
  879.     for (d = navail = 0; d < 4; d++)
  880.     {
  881.         x = ts.x + xincr[d*2]; y = ts.y + yincr[d*2];
  882.         if (!used[d] && POSSIBLE(x, y))
  883.         navail++;
  884.         else
  885.         used[d] = TRUE;
  886.     }
  887.     if (navail == 0)    /* no valid places for shots adjacent... */
  888.         goto refire;    /* ...so we must random-fire */
  889.     else
  890.     {
  891.         for (d = 0, n = rnd(navail) + 1; n; n--)
  892.         while (used[d])
  893.             d++;
  894.  
  895.         assert(d <= 4);
  896.  
  897.         used[d] = FALSE;
  898.         x = ts.x + xincr[d*2];
  899.         y = ts.y + yincr[d*2];
  900.  
  901.         assert(POSSIBLE(x, y));
  902.  
  903.         if (!(hit = cpufire(x, y)))
  904.         next = HUNT_DIRECT;
  905.         else
  906.         {
  907.         ts.x = x; ts.y = y; ts.dir = d*2; ts.hits++;
  908.         next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
  909.         }
  910.     }
  911.     break;
  912.  
  913.     case FIRST_PASS:    /* we have a start and a direction now */
  914.     x = ts.x + xincr[ts.dir];
  915.     y = ts.y + yincr[ts.dir];
  916.     if (POSSIBLE(x, y) && (hit = cpufire(x, y)))
  917.     {
  918.         ts.x = x; ts.y = y; ts.hits++;
  919.         next = (hit == S_SUNK) ? RANDOM_FIRE : FIRST_PASS;
  920.     }
  921.     else
  922.         next = REVERSE_JUMP;
  923.     break;
  924.  
  925.     case REVERSE_JUMP:    /* nail down the ship's other end */
  926.     d = ts.dir + 4;
  927.     x = ts.x + ts.hits * xincr[d];
  928.     y = ts.y + ts.hits * yincr[d];
  929.     if (POSSIBLE(x, y) && (hit = cpufire(x, y)))
  930.     {
  931.         ts.x = x; ts.y = y; ts.dir = d; ts.hits++;
  932.         next = (hit == S_SUNK) ? RANDOM_FIRE : SECOND_PASS;
  933.     }
  934.     else
  935.         next = RANDOM_FIRE;
  936.     break;
  937.  
  938.     case SECOND_PASS:    /* kill squares not caught on first pass */
  939.     x = ts.x + xincr[ts.dir];
  940.     y = ts.y + yincr[ts.dir];
  941.     if (POSSIBLE(x, y) && (hit = cpufire(x, y)))
  942.     {
  943.         ts.x = x; ts.y = y; ts.hits++;
  944.         next = (hit == S_SUNK) ? RANDOM_FIRE: SECOND_PASS;
  945.         break;
  946.     }
  947.     else
  948.         next = RANDOM_FIRE;
  949.     break;
  950.     }
  951.  
  952.     /* check for continuation and/or winner */
  953.     if (salvo)
  954.     {
  955.     (void)refresh();
  956.     (void)sleep(1);
  957.     }
  958.     if (awinna() != -1)
  959.     return(FALSE);
  960.  
  961. #ifdef DEBUG
  962.     (void) mvprintw(PROMPTLINE + 2, 0,
  963.             "New state %d, x=%d, y=%d, d=%d",
  964.             next, x, y, d);
  965. #endif /* DEBUG */
  966.     return(hit);
  967. }
  968.  
  969. playagain()
  970. {
  971.     int j;
  972.     ship_t *ss;
  973.  
  974.     for (ss = cpuship; ss < cpuship + SHIPTYPES; ss++)
  975.     for(j = 0; j < ss->length; j++)
  976.     {
  977.         cgoto(ss->y + j * yincr[ss->dir], ss->x + j * xincr[ss->dir]);
  978.         (void)addch((chtype)ss->symbol);
  979.     }
  980.  
  981.     if(awinna())
  982.     ++cpuwon;
  983.     else
  984.     ++plywon;
  985.     j = 18 + strlen(name);
  986.     if(plywon >= 10)
  987.     ++j;
  988.     if(cpuwon >= 10)
  989.     ++j;
  990.     (void) mvprintw(1,(COLWIDTH-j)/2,
  991.             "%s: %d     Computer: %d",name,plywon,cpuwon);
  992.  
  993.     prompt(2, (awinna()) ? "Want to be humiliated again, %s [yn]? "
  994.        : "Going to give me a chance for revenge, %s [yn]? ",name);
  995.     return(sgetc("YN") == 'Y');
  996. }
  997.  
  998. static void do_options(c,op)
  999. int c;
  1000. char *op[];
  1001. {
  1002.     register int i;
  1003.  
  1004.     if (c > 1)
  1005.     {
  1006.     for (i=1; i<c; i++)
  1007.     {
  1008.         switch(op[i][0])
  1009.         {
  1010.         default:
  1011.         case '?':
  1012.         (void) fprintf(stderr, "Usage: battle [-s | -b] [-c]\n");
  1013.         (void) fprintf(stderr, "\tWhere the options are:\n");
  1014.         (void) fprintf(stderr, "\t-s : play a salvo game\n");
  1015.         (void) fprintf(stderr, "\t-b : play a blitz game\n");
  1016.         (void) fprintf(stderr, "\t-c : ships may be adjacent\n");
  1017.         exit(1);
  1018.         break;
  1019.         case '-':
  1020.         switch(op[i][1])
  1021.         {
  1022.         case 'b':
  1023.             blitz = 1;
  1024.             if (salvo == 1)
  1025.             {
  1026.             (void) fprintf(stderr,
  1027.                 "Bad Arg: -b and -s are mutually exclusive\n");
  1028.             exit(1);
  1029.             }
  1030.             break;
  1031.         case 's':
  1032.             salvo = 1;
  1033.             if (blitz == 1)
  1034.             {
  1035.             (void) fprintf(stderr,
  1036.                 "Bad Arg: -s and -b are mutually exclusive\n");
  1037.             exit(1);
  1038.             }
  1039.             break;
  1040.         case 'c':
  1041.             closepack = 1;
  1042.             break;
  1043.         default:
  1044.             (void) fprintf(stderr,
  1045.                 "Bad arg: type \"%s ?\" for usage message\n", op[0]);
  1046.             exit(1);
  1047.         }
  1048.         }
  1049.     }
  1050.     }
  1051. }
  1052.  
  1053. static int scount(who)
  1054. int who;
  1055. {
  1056.     register int i, shots;
  1057.     register ship_t *sp;
  1058.  
  1059.     if (who)
  1060.     sp = cpuship;    /* count cpu shots */
  1061.     else
  1062.     sp = plyship;    /* count player shots */
  1063.  
  1064.     for (i=0, shots = 0; i < SHIPTYPES; i++, sp++)
  1065.     {
  1066.     if (sp->hits >= sp->length)
  1067.         continue;        /* dead ship */
  1068.     else
  1069.         shots++;
  1070.     }
  1071.     return(shots);
  1072. }
  1073.  
  1074. main(argc, argv)
  1075. int argc;
  1076. char *argv[];
  1077. {
  1078.     do_options(argc, argv);
  1079.  
  1080.     intro();
  1081.     do {
  1082.     initgame();
  1083.     while(awinna() == -1)
  1084.     {
  1085.         if (!blitz)
  1086.         {
  1087.         if (!salvo)
  1088.         {
  1089.                 if(turn)
  1090.             (void) cputurn();
  1091.             else
  1092.             (void) plyturn();
  1093.         }
  1094.         else
  1095.         {
  1096.             register int i;
  1097.  
  1098.             i = scount(turn);
  1099.             while (i--)
  1100.             {
  1101.             if (turn)
  1102.             {
  1103.                 if (cputurn() && awinna() != -1)
  1104.                 i = 0;
  1105.             }
  1106.             else
  1107.             {
  1108.                 if (plyturn() && awinna() != -1)
  1109.                 i = 0;
  1110.             }
  1111.             }
  1112.         } 
  1113.         }
  1114.         else
  1115.             while(turn ? cputurn() : plyturn())
  1116.             continue;
  1117.         turn = OTHER;
  1118.     }
  1119.     } while
  1120.     (playagain());
  1121.     uninitgame();
  1122.     /*NOTREACHED*/
  1123. }
  1124.  
  1125. /* bs.c ends here */
  1126.